<?php

namespace app\common\service;

use app\common\exception\BusinessException;
use app\common\helper\SSEHelper;

class BigModelService
{

    /**
     * apiKey
     * example:sk-uLqRB50NydVFEw4wXmhojg9MQ7m2T5cNhiWdlUhca6WwyE2l
     * @var string
     */
    private string $apiKey = '';

    /**
     * apiHost
     * @var string
     */
    private string $apiHost = 'https://open.bigmodel.cn';

    /**
     * @var string
     */
    private string $roleRule = '';

    /**
     * useModel
     * @var string
     */
    private string $useModel = 'glm-4-flash';

    /**
     * @var string
     */
    private string $response = '';

    private array $responseFormat = ['type' => 'text'];

    /**
     * @var string
     */
    private string $errorType = '';

    /**
     * @var string
     */
    private string $errorMessage = '';

    public function getApiKey(): string
    {
        return $this->apiKey;
    }

    public function setApiKey(string $apiKey): BigModelService
    {
        $this->apiKey = $apiKey;
        return $this;
    }

    public function getApiHost(): string
    {
        return $this->apiHost;
    }

    public function setApiHost(string $apiHost): BigModelService
    {
        $this->apiHost = $apiHost;
        return $this;
    }


    public function getRoleRule(): string
    {
        return $this->roleRule;
    }

    public function setRoleRule(string $roleRule): BigModelService
    {
        $this->roleRule = $roleRule;
        return $this;
    }

    public function getResponse(): string
    {
        return $this->response;
    }

    public function setResponse(string $response): void
    {
        $this->response = $response;
    }

    public function getErrorMessage(): string
    {
        return $this->errorMessage;
    }

    public function setErrorMessage(string $errorMessage): void
    {
        $this->errorMessage = $errorMessage;
    }

    public function getErrorType(): string
    {
        return $this->errorType;
    }

    public function setErrorType(string $errorType): void
    {
        $this->errorType = $errorType;
    }

    public function getUseModel(): string
    {
        return $this->useModel;
    }

    public function setUseModel(string $useModel): BigModelService
    {
        $this->useModel = $useModel;
        return $this;
    }

    /**
     * chat
     * @param $message
     * @return string
     * @throws BusinessException
     */
    public function chat($message): string
    {
        $url = $this->apiHost . '/api/paas/v4/chat/completions';
        $header = [
            'Content-Type: application/json',
            "Authorization: " . $this->apiKey,
        ];
        $postData = json_encode([
            "model" => $this->useModel,
            "messages" => [
                ["role" => "system", "content" => $this->roleRule],
                ["role" => "user", "content" => $message]
            ],
            "temperature" => 0.3,
            "response_format" => $this->responseFormat,
        ]);
        $response = $this->response = curl($url, $postData, false, $header);
        if (!$response) {
            throw new BusinessException('Ai服务异常:请求超时');
        }
        $responseData = json_decode($response, true);
        if (!$responseData) {
            throw new BusinessException('Ai服务异常:响应解析失败');
        }
        $code = $responseData['code'] ?? 0;
        $message = $responseData['message'] ?? '';
        if ($code && $code == '-2001') {
            throw new BusinessException('Ai服务异常:' . $message);
        }
        $errorType = $responseData['error']['type'] ?? '';
        $errorMessage = $responseData['error']['message'] ?? '';
        if ($errorMessage) {
            $this->errorType = $errorType;
            $this->errorMessage = $errorMessage;
            throw new BusinessException('Ai服务异常:' . $errorMessage);
        }

        // 返回
        return $responseData['choices']['0']['message']['content'] ?? '';
    }

    /**
     * invokeWithStream
     * @param $setAnswerRoleRule
     * @param $setQuestionContent
     * @return string
     * @throws
     */
    public function invokeWithStream($setAnswerRoleRule, $setQuestionContent)
    {
        // ai接口地址
        $url = 'https://api.moonshot.cn/v1/chat/completions';
        $MOONSHOT_API_KEY = 'sk-uLqRB50NydVFEw4wXmhojg9MQ7m2T5cNhiWdlUhca6WwyE2l';

        // ai接口header
        $header = [
            'Content-Type: application/json',
            "Authorization: Bearer " . $MOONSHOT_API_KEY,
        ];

        // ai接口参数
        $reqData = json_encode([
            "model" => "moonshot-v1-8k",
            "messages" => [
                ["role" => "system", "content" => $setAnswerRoleRule],
                ["role" => "user", "content" => $setQuestionContent]
            ],
            "temperature" => 0.3,
            "stream" => true
        ], 256);

        // 设置sse header
        SSEHelper::setHttpHeader();

        // 设置sse 回调
        $buffer = '';
        $allContent = '';
        curlWithStream($url, $header, $reqData, function ($ch, $str) use (&$buffer, &$allContent) {

            // 缓冲区数据+本次回调数据
            $buffer .= $str;

            // 按行分割缓冲区中的数据
            $lines = SSEHelper::splitMessageByLineChar($buffer);

            // 多余行保存到缓冲区下次处理
            if (count($lines) > 1) {
                $buffer = array_pop($lines);
            }
            foreach ($lines as $line) {
                // 创建属于自己的消息数据
                $newMessage = SSEHelper::createMessage();

                // 检查api异常
                $checkErrorData = json_decode($line, true);
                if (is_array($checkErrorData)) {
                    $error = $checkErrorData['error']['message'] ?? '';
                    if ($error) {
                        $newMessage['code'] = 1;
                        $newMessage['data'] = $error;
                        $newMessage['finish'] = true;
                        SSEHelper::flushMessage('data', $newMessage);
                        continue;
                    }
                }

                // 转换信息
                $message = SSEHelper::formatMessage($line);
                if (!$message) {
                    SSEHelper::flushFailMessage('转换信息失败', false);
                    continue;
                }

                // 提取参数
                $key = $message['key'];
                $val = $message['val'];

                // 检查key
                if ($key != 'data' || $val == '') {
                    continue;
                }

                // 是否结束
                $finish = strpos($val, '[DONE]') !== false;
                if ($finish) {
                    continue;
                }

                // 转换Json
                $responseData = json_decode($val, true);
                if (!is_array($responseData)) {
                    SSEHelper::flushFailMessage('转换Json失败', false);
                    continue;
                }
                $finish = $responseData['choices']['0']['finish_reason'] ?? '';
                $sendData = $responseData['choices']['0']['delta']['content'] ?? '';
                if ($sendData) {
                    $allContent .= $sendData;
                    SSEHelper::flushSuccessMessage($sendData, $finish ?: false);
                }
            }
            return strlen($str);
        });
        return $allContent;
    }

    public function getResponseFormat(): array
    {
        return $this->responseFormat;
    }

    public function setResponseFormat(array $responseFormat): void
    {
        $this->responseFormat = $responseFormat;
    }
}